require 'logger'
require 'parser/scope'

@@log = Logger.new(STDOUT)
@@log.progname = "Generator"
@@log.datetime_format = ""
@@log.level = Logger::INFO

class Generator

  def self.raw_line(text="")
    puts text
  end
  
  def self.write_line(inst="", comment="", indent=true)
    if(comment == "")
      raw_line("    %s" % inst)
    else
      raw_line("    %-38s//%s" % [inst, comment])
    end
  end

  # ===Program-Related===
  def self.start_program(name)
    @@log.debug "Called start_program(#{name})"

    raw_line("// This code was generated using the Contrived Compiler for")
    raw_line("//   Pr. Wilsey's 683 Compilers course at the University of Cincinnati")
    raw_line("#include <iostream>")
    raw_line("#include <stdio.h>")
    raw_line("using namespace std;")
    write_line
    sp  = Scope.next_register
    fp  = Scope.next_register
    ra  = Scope.next_register
    rv  = Scope.next_register
    acc  = Scope.next_register
    bcc  = Scope.next_register
    raw_line("int main(int argc, char** argv) {")
    write_line
    write_line("#define SP   #{sp}", "Define the Stack Pointer register index")
    write_line("#define FP   #{fp}", "Define the Frame Pointer register index")
    write_line("#define RV   #{rv}", "Define the Return Value register index")
    write_line("#define ACC  #{acc}", "Define a general purpose register")
    write_line("#define BCC  #{bcc}", "Define another general purpose  register")
    write_line
    write_line("int Reg[256];", "Initialize the register file")
    write_line("int MEM[32000];", "Initialize the memory space")
    write_line("Reg[SP] = 0;","Initialize the Stack Pointer")
    write_line("Reg[FP] = 0;","Initialize the Frame Pointer")
    write_line("Reg[RV] = 0;","Initialize the Return Value Register")
    write_line("Reg[ACC] = 0;","Initialize GPR A")
    write_line("Reg[BCC] = 0;","Initialize GPR B")
    write_line
    write_line("void * RA;", "Define the Return Address pointer")
    write_line
    runtime()
    write_line
    raw_line("/* ---Begin program #{name}--- */")
    write_line
    push_frame()
    raw_line("/* --Local Variables-- */")
  end
  
  def self.program_body()
    @@log.debug "Called program_body()"
    write_line("\n/* Program Body */")
  end
  
  def self.end_program(name)
    @@log.debug "Called end_program(#{name})"
    write_line
    raw_line("/* ---End program #{name}--- */")
    write_line
    write_line("return 0;")
    raw_line("}")
  end

  # ===Run-Time Support===
  def self.runtime()
    raw_line("/* ---Begin Run-Time Functions--- */")
    reg = Scope.next_register
    reg2 = Scope.next_register
    reg3 = Scope.next_register
    write_line("goto END_RUNTIME;", "Skip over run-time function declarations")
    raw_line("SCOPE0_put_Character:")
    push_frame()
    write_line("Reg[#{reg}] = MEM[Reg[FP] + 2];", "Load the address of arg 1")
    write_line("Reg[#{reg}] = MEM[Reg[#{reg}]];", "Load indirect arg 1")
    write_line("cout << (char)Reg[#{reg}] << endl;", "Dereference pointer and cast to a char")
    pop_frame()
    write_line
    raw_line("SCOPE0_put_String:")
    push_frame()
    write_line("Reg[#{reg}] = MEM[Reg[FP] + 2];", "Load the address of arg 1")
    write_line("Reg[#{reg}] = MEM[Reg[#{reg}]];", "Load indirect arg 1")
    write_line("cout << (char *)Reg[#{reg}] << endl;", "Dereference pointer and cast to a char *")
    pop_frame()
    write_line
    raw_line("SCOPE0_put_Integer:")
    push_frame()
    write_line("Reg[#{reg}] = MEM[Reg[FP] + 2];", "Load the address of arg 1")
    write_line("Reg[#{reg}] = MEM[Reg[#{reg}]];", "Load indirect arg 1")
    write_line("cout << (int)Reg[#{reg}] << endl;", "Dereference pointer and cast to a int")
    pop_frame()
    write_line
    raw_line("SCOPE0_get_Integer:")
    push_frame()
    write_line("cin >> Reg[RV];", "Read an Integer")
    pop_frame()
    write_line
    raw_line("SCOPE0_get_Character:")
    push_frame()
    write_line("char c;", "temp space for a char")
    write_line("cin >> c;", "Read a character")
    write_line("Reg[RV] = (int)c;", "store the character")
    pop_frame()
    write_line
    raw_line("SCOPE0_int2char_Integer:")
    push_frame()
    write_line("Reg[#{reg}] = MEM[Reg[FP] + 2];", "Load the address of arg 1")
    write_line("Reg[RV] = (char)(MEM[Reg[#{reg}]]);", "Load indirect arg 1")
    pop_frame()
    write_line
    raw_line("SCOPE0_char2int_Character:")
    push_frame()
    write_line("Reg[#{reg}] = MEM[Reg[FP] + 2];", "Load the address of arg 1")
    write_line("Reg[RV] = (int)(MEM[Reg[#{reg}]]);", "Load indirect arg 1")
    pop_frame()
    write_line
    raw_line("SCOPE0_sqrt_Integer:")
    push_frame()
    write_line("Reg[#{reg}] = MEM[Reg[FP] + 2];", "Load the address of arg 1")
    write_line("Reg[#{reg}] = (int)(MEM[Reg[#{reg}]]);", "Load indirect arg 1")
    write_line("Reg[#{reg2}] = 1;", "Approximate sqrt by computing squares.")
    raw_line("sqrt_loop:")
    write_line("Reg[#{reg3}] = Reg[#{reg2}] * Reg[#{reg2}];", "Compute a square.")
    write_line("if(Reg[#{reg3}] > Reg[#{reg}]) goto sqrt_done;", "Found the sqrt.")
    write_line("Reg[#{reg2}] = Reg[#{reg2}] + 1;", "Otherwise, increment...")
    write_line("goto sqrt_loop;", "...and try again.")
    raw_line("sqrt_done:")
    write_line("Reg[RV] = Reg[#{reg2}] - 1;", "")
    pop_frame()
    write_line
    raw_line("END_RUNTIME:")
  end
  
  # ===Scoping===
  def self.push_frame()
    @@log.debug "Called push_frame()"
    
    raw_line("/* --Push a new stack frame-- */")
    write_line("Reg[SP] = Reg[SP] + 1;", "Allocate space for the Frame Pointer")
    write_line("MEM[Reg[SP]] = Reg[FP];", "Push the previous Frame Pointer")
    write_line("Reg[FP] = Reg[SP];", "Move the Frame Pointer to the Stack bottom")
    write_line("Reg[SP] = Reg[SP] + 1;", "Allocate space for the Return Address")
  end
  
  def self.pop_frame()
    Generator.write_line("RA = (void *)MEM[Reg[FP] + 1];", "Get the return address from the stack frame")
    Generator.write_line("Reg[SP] = Reg[FP];", "Restore the previous Stack Pointer")
    Generator.write_line("Reg[FP] = MEM[Reg[FP]];", "Restore the previous Frame Pointer")
    Generator.write_line("goto *RA;", "Return to the return address")
  end
  
  # ===Expression-Parsing===
  def self.operation(operand_a, operator, operand_b)
    @@log.debug "Called operation(#{operand_a}, #{operator}, #{operand_b})"
    write_line("Reg[#{operand_a}] = Reg[#{operand_a}] #{operator} Reg[#{operand_b}];", "Perform #{operator} operation")
    return operand_a
  end
  
  def self.literal(literal_type, literal, reg)
    @@log.debug "Called literal(#{literal_type}, #{literal}, #{reg})"
    if(literal_type == :String)
      (reg, var) = reg
      write_line("Reg[#{reg}] = (int)(#{literal});", "")
    else
      write_line("Reg[#{reg}] = #{literal};", "Load #{literal_type} literal")
    end
  end
end